#ifndef HTMLPARSER
#define HTMLPARSER
#include <iostream>
#include <map>
#include "../../base/TemplateParser/Direction.h"
#include "../../base/StringProcessor/StringProcessor.h"
using namespace std;
class HtmlParser: public Direction
{
	public:
		string parse(string);
		string getViewData(string);
		boost::any getViewAnyData(string);
		void setViewData(string, boost::any);

	private:
		boost::any setDataToArr(boost::any, string, boost::any);
		boost::any getDataFromArr(boost::any,string);
 };

string HtmlParser::parse(string html){
	string out;
	//check direction
	string::size_type pos=html.find_first_of('@');
	if(pos!=string::npos){
		out=html.substr(0,pos);
		html=html.substr(pos+1,html.size()-(pos+1));
		//get direction:
		BO::parseitem dir;
		//for string "" check
		bool isInString=false;
		string breakstring=" \t\n\r{";//string to break direction
		string opstring="=+-*/%";//string to check direction is a Operation
		string::iterator it=html.begin();
		pos=0;
		while(it!=html.end()){
			//check string
			if(*it=='"' && *(it-1)!='\\')isInString=!isInString;
			//get whole direction as : if(...), while(...), int(...)
			if(!isInString && StringProcessor::isInString(*it,breakstring)){
				dir.direction=html.substr(0,pos);
				//check if current break is space, then remove space
				if(StringProcessor::isInString(*it," \r\n\t"))pos++;
				//get remain html
				dir.in=html.substr(pos,html.size()-pos);
				break;
			}
			it++;
			pos++;
		}
		//parse operation
		if(isOperation(dir.direction))parseOperation(dir.direction);
		//parse direction
		else if(isDirection(dir.direction)){
			//get content
			//check is if direction
			bool isIf=false;
			if(dir.direction.substr(0,2)=="if")isIf=true;
			pos=string::npos;//reset pos
			pos=dir.in.find_first_of('{');
			if(pos==string::npos)throw "expected { in direction "+dir.direction;
			pos++;
			string::size_type posstart=pos;
			it=dir.in.begin()+pos;
			int openbracket=1;
			while(it!=dir.in.end()){
				if(*it=='{')openbracket++;
				else if(*it=='}')openbracket--;
				if(openbracket==0){
					//check if
					if(isIf){
						//increase position it
						it++;
						//check elseif
						string::size_type pos2=pos+1;//bc of it++
						while(it!=dir.in.end()){
							//check direction
							if(*it=='@'){
								//increase position it
								it++;
								//check if else or elseif
								string::size_type pos3=pos2+1;//bc of it++

								while(it!=dir.in.end()){
									if(StringProcessor::isInString(*it,breakstring+"(")){
										//get direction
										string direction=dir.in.substr(pos2,pos3-pos2);
										if(direction=="@else"||direction=="@elseif"){
											//find first of {
											string::size_type pos4=dir.in.find_first_of('{',pos3);
											//goto this position and continue get content
											if(pos4==string::npos)throw "expected { in direction "+direction;
											pos=pos4;
											it=dir.in.begin()+pos;
											openbracket++;
											//set isIf
											if(direction=="@else")isIf=false;
											goto contloop;
										}
										goto exitloop;
									}
									it++;
									pos3++;
								}
								//end of string
								goto exitloop;
							}
							//skip space and endl
							else if(!StringProcessor::isInString(*it," \r\n\t")){
								goto exitloop;
							}
							pos2++;
							it++;
						}
						//end of string
						goto exitloop;
					}
					else
						break;
				}
contloop:		pos++;
				it++;
			}
exitloop:	if(openbracket>0)throw "expected } in direction "+dir.direction;
			string content;

			if(dir.direction.substr(0,2)=="if")
				content=dir.in.substr(0,pos+1);
			else
				content=dir.in.substr(posstart,pos-posstart);
			pos++;
			dir.in=dir.in.substr(pos,dir.in.size()-pos);
			//cout<<dir.direction+"-"+content+"-"+dir.in;
			out+=parseDirection(dir.direction, content);
		}
		//parse function
		else if(isFunction(dir.direction))out+=parseFunction(dir.direction);
		//echo view
		else
			out+=getViewData(dir.direction);

		if(dir.in!="")out+=parse(dir.in);
		//cout<<dir.direction+"-"+dir.in<<endl;
	}
	//else return in
	else out=html;
	return out;
}
boost::any HtmlParser::getViewAnyData(string key){
	//cout<<key<<"-getViewData"<<endl;
	//parse array []
	string::iterator it=key.begin();
	string::size_type posstart=0;
	string::size_type pos=0;
	int openbracket=0;
	while(it!=key.end()){
		if(*it=='['){
			if(openbracket==0)posstart=pos;
			openbracket++;
		}
		else if(*it==']'){
			openbracket--;
			if(openbracket==0)break;
		}
		pos++;
		it++;
	}
	//check error
	if(openbracket>0){
		//throw error
		throw "expected ]";
	}
	//if key is array element then
	else if(it!=key.end()){
		string keyarray=key.substr(0,posstart);
		posstart++;
		string element=key.substr(posstart,pos-posstart);
		//check is array
		map<string,boost::any> arrData;
		try{
			//arrData=ViewData[keyarray].cast<map<string, any> >();
			arrData=boost::any_cast<map<string, boost::any> >(ViewData[keyarray]);
		}
		catch(...){
			throw "element " + keyarray + " is not an array";
		}
		//parse Operation
		element=parseOperation(element);
		//cout<<keyarray<<'-'<<element<<endl;
		//get element data
		boost::any eleData=arrData[element];

		//parse array in remain if not empty
		string remain=key.substr(pos+1,key.size()-(pos+1));
		if(remain!="")return getDataFromArr(eleData, remain);
		else{
			//return eleData
			return eleData;
		}

	}
	//if no array then return view data
	//cout<<key<<"-out-getViewData"<<endl;
	return ViewData[key];
}

string HtmlParser::getViewData(string key){
	try{
		//return getViewAnyData(key).cast<string>();
		return boost::any_cast<string>(getViewAnyData(key));
	}
	catch(...){
		throw "data "+ key + " is not a string value";
	}
	return "";
}
boost::any HtmlParser::setDataToArr(boost::any in, string key,boost::any value){
	map<string,boost::any> arrData;
	//parse array []
	string::iterator it=key.begin();
	string::size_type posstart=0;
	string::size_type pos=0;
	int openbracket=0;
	while(it!=key.end()){
		if(*it=='['){
			if(openbracket==0)posstart=pos;
			openbracket++;
		}
		else if(*it==']'){
			openbracket--;
			if(openbracket==0)break;
		}
		pos++;
		it++;
	}
	//check error
	if(openbracket>0){
		//throw error
		throw "expected ]";
	}
	//if key is array element then
	else if(it!=key.end()){
		string element=key.substr(posstart+1,pos-(posstart+1));

		//check if in is array

		try{
			//arrData=in.cast<map<string, any> >();
			arrData=boost::any_cast<map<string, boost::any> >(in);
		}
		catch(...){
			//throw "element "+element+" is not an array";
		}

		//check if element is empty ( a[]=b ) then init with default key: 0,1,2....
		if(element==""){
			int i=0;
			do{
				element=StringProcessor::itoa(i);
				i++;
			}
			while(arrData.count(element)>0);
		}
		//parse Operation
		else{
			element=parseOperation(element);
		}
		//cout<<key<<'-'<<element<<"set"<<endl;
		//parse array in remain if not empty
		string remain=key.substr(pos+1,key.size()-(pos+1));
		if(remain!="")arrData[element]=setDataToArr(arrData[element], remain, value);
		else{
			//set data
			arrData[element]=value;
		}
	}
	else{
		//if no array then throw error
		throw "element "+ key + " is not a element";
	}

	return arrData;
}

void HtmlParser::setViewData(string key,boost::any value){
	//parse array []
	string::iterator it=key.begin();
	string::size_type posstart=0;
	string::size_type pos=0;
	int openbracket=0;
	while(it!=key.end()){
		if(*it=='['){
			if(openbracket==0)posstart=pos;
			openbracket++;
		}
		else if(*it==']'){
			openbracket--;
			if(openbracket==0)break;
		}
		pos++;
		it++;
	}
	//check error
	if(openbracket>0){
		//throw error
		throw "expected ]";
	}
	//if key is array element then
	else if(it!=key.end()){
		string keyarray=key.substr(0,posstart);
		string element=key.substr(posstart+1,pos-(posstart+1));

		map<string,boost::any> arrData;

		//check if key exist
		if(ViewData.count(keyarray)>0){
			//check if is array
			try{
				arrData=boost::any_cast<map<string, boost::any> >(ViewData[keyarray]);
			}
			catch(...){
				//if not then overwrite current value
				//ViewData[keyarray]=arrData;
			}
		}


		//check if element is empty ( a[]=b ) then init with default key: 0,1,2....
		if(element==""){
			int i=0;
			do{
				element=StringProcessor::itoa(i);
				i++;
			}
			while(arrData.count(element)>0);
		}
		//parse Operation
		else{
			element=parseOperation(element);
		}
		//cout<<keyarray<<'-'<<element<<"set"<<endl;
		//parse array in remain if not empty

		string remain=key.substr(pos+1,key.size()-(pos+1));
		if(remain!="")arrData[element]=setDataToArr(arrData[element], remain, value);
		else{
			//set data
			//cout<<"set "+ element + "="<<endl;
			arrData[element]=value;
		}
		ViewData[keyarray]=arrData;
	}
	//if no array then set data
	else{
		ViewData[key]=value;
	}
}
boost::any HtmlParser::getDataFromArr(boost::any in, string ele)
{
	//parse array []
	string::iterator it=ele.begin();
	string::size_type posstart=0;
	string::size_type pos=0;
	int openbracket=0;
	while(it!=ele.end()){
		if(*it=='['){
			if(openbracket==0)posstart=pos;
			openbracket++;
		}
		else if(*it==']'){
			openbracket--;
			if(openbracket==0)break;
		}
		pos++;
		it++;
	}
	//check grammar
	if(openbracket>0){
		//throw error
		throw "expected ]";
	}
	//if key is array element then
	else if(it!=ele.end()){
		string element=ele.substr(posstart+1,pos-(posstart+1));
		cout<<ele+"-"+element+"-any";
		//check is array
		map<string,boost::any> arrData;
		try{
			//arrData=in.cast<map<string, any> >();
			arrData=boost::any_cast<map<string, boost::any> >(in);
		}
		catch(...){
			throw "element "+element+" is not an array";
		}
		//parse Operation
		element=parseOperation(element);
		//get element data
		//get element data
		boost::any eleData=arrData[element];
		//parse array in remain if not empty
		string remain=ele.substr(pos+1,ele.size()-(pos+1));
		if(remain!="")return getDataFromArr(eleData, remain);
		else{
			return eleData;
		}

	}
	//if no array then throw exception
	throw "element "+ele+" is not an array";
	return "";
}

#endif
